// Copyright (c) Microsoft Corporation. 
// Licensed under the MIT license.

#include "StandInsOutlinerWidgetItem.h"
#include "StandInsOutlinerDragDropOp.h"
#include "StandInMeshActorItem.h"
#include "StaticMeshActorItem.h"
#include "StandInsOutliner.h"
#include "SlateOptMacros.h"
#include "Widgets/Input/SButton.h"
#include "SVisibilityButton.h"
#include "Widgets/Input/SCheckBox.h"


#define LOCTEXT_NAMESPACE "HLODTreeWidgetItem"

namespace StandInsOutliner
{
	static void UpdateOperationDecorator(const FDragDropEvent& Event, const FDragValidationInfo& ValidationInfo)
	{
		const FSlateBrush* Icon = ValidationInfo.IsValid() ? FEditorStyle::GetBrush(TEXT("Graph.ConnectorFeedback.OK")) : FEditorStyle::GetBrush(TEXT("Graph.ConnectorFeedback.Error"));

		FDragDropOperation* Operation = Event.GetOperation().Get();
		if (Operation)
		{
			if (Operation->IsOfType<FStandInsOutlinerDragDropOp>())
			{
				auto* OutlinerOp = static_cast<FStandInsOutlinerDragDropOp*>(Operation);
				OutlinerOp->SetTooltip(ValidationInfo.ValidationText, Icon);
			}
			else if (Operation->IsOfType<FActorDragDropGraphEdOp>())
			{
				auto* ActorOp = static_cast<FActorDragDropGraphEdOp*>(Operation);

				if (ValidationInfo.IsValid())
				{
					ActorOp->SetToolTip(FActorDragDropGraphEdOp::ToolTip_CompatibleGeneric, ValidationInfo.ValidationText);
				}
				else
				{
					ActorOp->SetToolTip(FActorDragDropGraphEdOp::ToolTip_IncompatibleGeneric, ValidationInfo.ValidationText);
				}
			}
		}
	}

	static void ResetOperationDecorator(const FDragDropEvent& Event)
	{
		FDragDropOperation* Operation = Event.GetOperation().Get();
		if (Operation)
		{
			if (Operation->IsOfType<FActorDragDropGraphEdOp>())
			{
				static_cast<FActorDragDropGraphEdOp*>(Operation)->ResetToDefaultToolTip();
			}
		}
	}

	static FReply OnDragDetected(const FGeometry& /*MyGeometry*/, const FPointerEvent& MouseEvent, TWeakPtr<STableViewBase> Table)
	{
		auto TablePtr = Table.Pin();
		if (TablePtr.IsValid() && MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton))
		{
			auto TreeView = (STreeView< FTreeItemPtr >*)TablePtr.Get();
			auto SelectedItems = TreeView->GetSelectedItems();
			auto Operation = StandInsOutliner::CreateDragDropOperation(SelectedItems);
			if (Operation.IsValid())
			{
				return FReply::Handled().BeginDragDrop(Operation.ToSharedRef());
			}
		}

		return FReply::Unhandled();
	}

	FReply HandleDrop(TWeakPtr<STableViewBase> Widget, const FDragDropEvent& DragDropEvent, IDropTarget& DropTarget, FDragValidationInfo& ValidationInfo, SStandInsOutlinerWidgetItem* DroppedWidget, bool bApplyDrop)
	{
		FDragDropPayload DraggedObjects;
		// Validate now to make sure we don't doing anything we shouldn't
		if (!DraggedObjects.ParseDrag(*DragDropEvent.GetOperation()))
		{
			// Invalid selection
			ValidationInfo = FDragValidationInfo(EStandInsOutlinerActionType::InvalidAction, FStandInsOutlinerDragDropOp::ToolTip_Incompatible, LOCTEXT("InvalidSelection", "Selection contains already clustered Actor(s)"));
			return FReply::Unhandled();
		}

		ValidationInfo = DropTarget.ValidateDrop(DraggedObjects);

		if (!ValidationInfo.IsValid())
		{
			// Return handled here to stop anything else trying to handle it - the operation is invalid as far as we're concerned
			return FReply::Handled();
		}

		if (bApplyDrop)
		{
			DraggedObjects.OutlinerWorld = DroppedWidget->GetWorld();
			DropTarget.OnDrop(DraggedObjects, ValidationInfo, Widget.Pin().ToSharedRef());
		}

		return FReply::Handled();
	}

	BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
		void SStandInsOutlinerWidgetItem::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwner)
	{
		this->TreeItem = InArgs._TreeItemToVisualize.Get();
		this->Outliner = InArgs._Outliner;
		this->World = InArgs._World;
		check(TreeItem);

		WeakTableViewBase = InOwner;

		auto Args = FSuperRowType::FArguments()
			.Padding(FMargin(1.0f))
			.OnDragDetected_Static(StandInsOutliner::OnDragDetected, TWeakPtr<STableViewBase>(InOwner));

		SMultiColumnTableRow< FTreeItemPtr >::Construct(Args, InOwner);
	}


	TSharedRef<SWidget> SStandInsOutlinerWidgetItem::GenerateWidgetForColumn(const FName& ColumnName)
	{
		if (ColumnName == TEXT("SceneActorName"))
		{
			return SNew(SHorizontalBox)
				+ SHorizontalBox::Slot()
				.AutoWidth()
				.VAlign(VAlign_Center)
				[
					SNew(SExpanderArrow, SharedThis(this))
				]

			+ SHorizontalBox::Slot()
				.AutoWidth()
				.Padding(2.0f, 0.0f)
				.VAlign(VAlign_Center)
				[
					SNew(STextBlock)
					.Text(this, &SStandInsOutlinerWidgetItem::GetItemDisplayString)
					.ColorAndOpacity(this, &SStandInsOutlinerWidgetItem::GetTint)
				];
		}
		else if (ColumnName == TEXT("EditorVisibility"))
		{
			return SNew(SVisibilityButton)
				.ContentPadding(FMargin(0.0f))
				.ButtonStyle(FEditorStyle::Get(), "ToggleButton")
				.OnClicked(this, &SStandInsOutlinerWidgetItem::OnToggleSingleActorVisibility)
				.OnRightClicked(this, &SStandInsOutlinerWidgetItem::OnToggleStandinVisibility)
				.OnHovered(this, &SStandInsOutlinerWidgetItem::OnVisibilityToggleHovered)
				.OnUnhovered(this, &SStandInsOutlinerWidgetItem::OnVisibilityToggleUnhovered)
				.ToolTipText(this, &SStandInsOutlinerWidgetItem::GetVisibilityToolTip)
				.HAlign(HAlign_Center)
				.VAlign(VAlign_Center)
				.Content()
				[
					SNew(SImage)
					.Image(this, &SStandInsOutlinerWidgetItem::GetEditorVisibilityBrush)
				];
		}
		else if (TreeItem->GetTreeItemType() == ITreeItem::StaticMeshActor)
		{
			if (ColumnName == TEXT("UserData"))
			{
				FStaticMeshActorItem* Item = static_cast<FStaticMeshActorItem*>(TreeItem);

				return SNew(SHorizontalBox)
					+ SHorizontalBox::Slot()
					.AutoWidth()
					.Padding(4.0f, 2.0f)
					.VAlign(VAlign_Center)
					[
						SNew(STextBlock)
						.Text(Item, &FStaticMeshActorItem::GetSimplygonUserDataTagsAsText)
						.ColorAndOpacity(this, &SStandInsOutlinerWidgetItem::GetTint)
					];
			}
			
		}
		else if (TreeItem->GetTreeItemType() == ITreeItem::StandInMeshActor)
		{
			if (ColumnName == TEXT("StandinGame"))
			{
				FStandInMeshActorItem* Item = static_cast<FStandInMeshActorItem*>(TreeItem);

				return SNew(SHorizontalBox)
					+ SHorizontalBox::Slot()
					.VAlign(VAlign_Center)
					.HAlign(HAlign_Center)
					[
						SNew(SCheckBox)
						.ToolTipText(LOCTEXT("StandinGameCheckBoxToolTip", "Enabled: source actors will removed during cooking and replaced by the standin actor."))
						.IsChecked(Item, &FStandInMeshActorItem::IsGameStandin)
						.OnCheckStateChanged(Item, &FStandInMeshActorItem::OnGameStandinChanged)
					];
					
			}
			else if (ColumnName == TEXT("Level"))
			{
				FStandInMeshActorItem* Item = static_cast<FStandInMeshActorItem*>(TreeItem);

				return SNew(SHorizontalBox)
					+ SHorizontalBox::Slot()
					.AutoWidth()
					.Padding(4.0f, 2.0f)
					.VAlign(VAlign_Center)
					[
						SNew(STextBlock)
						.Text(Item, &FStandInMeshActorItem::GetLevelAsText)
						.ColorAndOpacity(this, &SStandInsOutlinerWidgetItem::GetTint)
					];
			}
			else if (ColumnName == TEXT("SubActors"))
			{
				FStandInMeshActorItem* Item = static_cast<FStandInMeshActorItem*>(TreeItem);

				return SNew(SHorizontalBox)
					+ SHorizontalBox::Slot()
					.FillWidth(1.0f)
					.VAlign(VAlign_Center)
					.HAlign(HAlign_Center)
					.Padding(4.0f, 2.0f)
					[
						SNew(STextBlock)
						.Text(Item, &FStandInMeshActorItem::GetNumberOfSubActorsAsText)
						.ColorAndOpacity(this, &SStandInsOutlinerWidgetItem::GetTint)
					];
			}
			else if (ColumnName == TEXT("TriangleCount"))
			{
				FStandInMeshActorItem* Item = static_cast<FStandInMeshActorItem*>(TreeItem);

				return SNew(SHorizontalBox)
					+ SHorizontalBox::Slot()
					.FillWidth(1.0f)
					.VAlign(VAlign_Center)
					.HAlign(HAlign_Center)
					.Padding(4.0f, 2.0f)
					[
						SNew(STextBlock)
						.Text(Item, &FStandInMeshActorItem::GetNumTrianglesAsText)
						.ColorAndOpacity(this, &SStandInsOutlinerWidgetItem::GetTint)
						.ToolTipText(LOCTEXT("StandinGameCheckBoxToolTip", "Standin triangle count / Total source triangle count"))
					];
			}
			/*else if (ColumnName == TEXT("LODGroup"))
			{
				FStandInMeshActorItem* Item = static_cast<FStandInMeshActorItem*>(TreeItem);

				return SNew(SHorizontalBox)
					+ SHorizontalBox::Slot()
					.AutoWidth()
					.VAlign(VAlign_Center)
					.Padding(4.0f, 2.0f)
					[
						SNew(STextBlock)
						.Text(Item, &FStandInMeshActorItem::GetLODGroupdAsText)
						.ColorAndOpacity(this, &SStandInsOutlinerWidgetItem::GetTint)
					];
			}*/
			else if (ColumnName == TEXT("ProcessType"))
			{
				FStandInMeshActorItem* Item = static_cast<FStandInMeshActorItem*>(TreeItem);

				return SNew(SHorizontalBox)
					+ SHorizontalBox::Slot()
					.FillWidth(1.0f)
					.VAlign(VAlign_Center)
					.HAlign(HAlign_Center)
					.Padding(4.0f, 2.0f)
					[
						SNew(STextBlock)
						.Text(Item, &FStandInMeshActorItem::GetStandInProcessTypeAsText)
						.ColorAndOpacity(this, &SStandInsOutlinerWidgetItem::GetTint)
					];
			}
		}

		return SNullWidget::NullWidget;
	}

	END_SLATE_FUNCTION_BUILD_OPTIMIZATION

		FText SStandInsOutlinerWidgetItem::GetItemDisplayString() const
	{
		return FText::FromString(TreeItem->GetDisplayString());
	}

	const FSlateBrush* SStandInsOutlinerWidgetItem::GetEditorVisibilityBrush() const
	{
		if (!TreeItem->IsHiddenInEditor())
		{
			return FEditorStyle::GetBrush("Level.VisibleIcon16x");
		}
		else
		{
			return FEditorStyle::GetBrush("Level.NotVisibleIcon16x");
		}
	}

	FReply SStandInsOutlinerWidgetItem::OnToggleStandinVisibility()
	{
		auto ChangeStandinVisibility = [](ITreeItem* TreeItem)
		{
			FStandInMeshActorItem* StandinItem = static_cast<FStandInMeshActorItem*>(TreeItem);
			StandinItem->SetIsVisibleInEditor(StandinItem->IsHiddenInEditor());
		};

		if (TreeItem->GetTreeItemType() == ITreeItem::StandInMeshActor)
		{
			ChangeStandinVisibility(TreeItem);
		}
		else if (TreeItem->GetTreeItemType() == ITreeItem::StaticMeshActor)
		{
			FStaticMeshActorItem* StaticMeshItem = static_cast<FStaticMeshActorItem*>(TreeItem);
			
			for (auto StandinItem : StaticMeshItem->GetParents())
			{
				if (!StandinItem.IsValid())
					continue;
				auto StandinItemPinned = StandinItem.Pin();
				if (StandinItemPinned && StandinItemPinned->GetTreeItemType() == ITreeItem::StandInMeshActor)
				{
					ChangeStandinVisibility(StandinItemPinned.Get());
				}
			}
			
		}
		return FReply::Handled();
	}

	FText SStandInsOutlinerWidgetItem::GetVisibilityToolTip() const
	{
		FString tooltip;
		tooltip.Append("Left-Click: Change individual actor visibility in level editor.");
		tooltip.Append("\nRight-Click: Toggle visibility between standin and source actors");
		tooltip.Append("\n\nNote: Changing this visibility will not affect the finial visibilty in a cooked game.");
		return FText::FromString( tooltip );
	}

	FReply SStandInsOutlinerWidgetItem::OnToggleSingleActorVisibility()
	{
		if (TreeItem->GetTreeItemType() == ITreeItem::StandInMeshActor)
		{
			FStandInMeshActorItem* StandinItem = static_cast<FStandInMeshActorItem*>(TreeItem);
			StandinItem->SetHiddenInEditor(!StandinItem->IsHiddenInEditor());
		}
		else if (TreeItem->GetTreeItemType() == ITreeItem::StaticMeshActor)
		{
			FStaticMeshActorItem* StaticMeshItem = static_cast<FStaticMeshActorItem*>(TreeItem);
			StaticMeshItem->SetHiddenInEditor(!StaticMeshItem->IsHiddenInEditor());
		}
		return FReply::Handled();
	}

	void SStandInsOutlinerWidgetItem::OnVisibilityToggleHovered()
	{
		if (TreeItem != nullptr)
		{
			TreeItem->bCanGenerateContextMenu = false;
		}
	}

	void SStandInsOutlinerWidgetItem::OnVisibilityToggleUnhovered()
	{
		if (TreeItem != nullptr)
		{
			TreeItem->bCanGenerateContextMenu = true;
		}
	}

	FSlateColor SStandInsOutlinerWidgetItem::GetTint() const
	{
		return TreeItem->GetTint();
	}

	void SStandInsOutlinerWidgetItem::OnDragEnter(FGeometry const& /*MyGeometry*/, FDragDropEvent const& DragDropEvent)
	{
		if (TreeItem)
		{
			FDragValidationInfo ValidationInfo = FDragValidationInfo::Invalid();

			StandInsOutliner::HandleDrop(WeakTableViewBase, DragDropEvent, *TreeItem, ValidationInfo, this, false);
			UpdateOperationDecorator(DragDropEvent, ValidationInfo);
		}
	}

	void SStandInsOutlinerWidgetItem::OnDragLeave(FDragDropEvent const& DragDropEvent)
	{
		ResetOperationDecorator(DragDropEvent);
	}

	FReply SStandInsOutlinerWidgetItem::OnDragOver(const FGeometry& /*MyGeometry*/, const FDragDropEvent&)
	{
		return FReply::Handled();
	}

	FReply SStandInsOutlinerWidgetItem::OnDrop(const FGeometry& /*MyGeometry*/, const FDragDropEvent& DragDropEvent)
	{
		if (TreeItem)
		{
			FDragValidationInfo ValidationInfo = FDragValidationInfo::Invalid();
			FReply Reply =  StandInsOutliner::HandleDrop(WeakTableViewBase, DragDropEvent, *TreeItem, ValidationInfo, this, true);
			this->Outliner->FullRefresh();
			return Reply;
		}

		return FReply::Unhandled();
	}
};

#undef LOCTEXT_NAMESPACE 
